// https://learn.microsoft.com/en-us/typography/opentype/spec/gpos

use super::super::ggg::*;
use super::*;

pub use kind01::*;
pub use kind02::*;
pub use kind03::*;
pub use kind04::*;
pub use kind05::*;
pub use kind06::*;

#[derive(Debug, Clone)]
pub struct Table<'a> {
	pub major_version: u16, // Major version of the GPOS table
	pub minor_version: u16, // Minor version of the GPOS table
	// pub script_list_offset: Option<Offset16>, // Offset to ScriptList table, from beginning of GPOS table.
	pub script_list: Option<ScriptList<'a>>,
	// pub feature_list_offset: Option<Offset16>, // Offset to FeatureList table, from beginning of GPOS table.
	pub feature_list: Option<FeatureList<'a>>,
	// pub lookup_list_offset: Option<Offset16>, // Offset to LookupList table, from beginning of GPOS table.
	pub lookup_list: Option<LookupList<'a, Subtable<'a>>>,
	// Offset to FeatureVariations table, from beginning of GPOS table (may be NULL).
	pub feature_variations_offset: Option<FeatureVariations<'a>>,
}
impl<'a> Table<'a> {
	pub fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let major_version = s.read()?;
		let minor_version = s.read()?;
		let this = Table {
			major_version,
			minor_version,
			script_list: s.parse_at_offset16(data),
			feature_list: s.parse_at_offset16(data),
			lookup_list: s.parse_at_offset16(data),
			feature_variations_offset: if major_version >= 1 && minor_version >= 1 {
				s.parse_at_offset32(data)
			} else {
				None
			},
		};
		Some(this)
	}
}

/// A glyph positioning
/// [lookup subtable](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#table-organization)
/// enumeration.
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub enum Subtable<'a> {
	Single(SingleAdjustment<'a>),
	Pair(PairAdjustment<'a>),
	Cursive(CursiveAdjustment<'a>),
	MarkToBase(MarkToBaseAdjustment<'a>),
	MarkToLigature(MarkToLigatureAdjustment<'a>),
	MarkToMark(MarkToMarkAdjustment<'a>),
	Context(ContextLookup<'a>),
	ChainContext(ChainedContextLookup<'a>),
}
impl<'a> LookupSubtable<'a> for Subtable<'a> {
	fn parse(data: &'a [u8], kind: u16) -> Option<Self> {
		match kind {
			1 => SingleAdjustment::parse(data).map(Self::Single),
			2 => PairAdjustment::parse(data).map(Self::Pair),
			3 => CursiveAdjustment::parse(data).map(Self::Cursive),
			4 => MarkToBaseAdjustment::parse(data).map(Self::MarkToBase),
			5 => MarkToLigatureAdjustment::parse(data).map(Self::MarkToLigature),
			6 => MarkToMarkAdjustment::parse(data).map(Self::MarkToMark),
			7 => ContextLookup::parse(data).map(Self::Context),
			8 => ChainedContextLookup::parse(data).map(Self::ChainContext),
			9 => parse_extension_lookup(data, Self::parse),
			_ => None,
		}
	}
}
impl<'a> Subtable<'a> {
	/// Returns the subtable coverage.
	#[inline]
	pub fn coverage(&self) -> Coverage<'a> {
		match self {
			Self::Single(t) => t.coverage(),
			Self::Pair(t) => t.coverage(),
			Self::Cursive(t) => t.coverage,
			Self::MarkToBase(t) => t.mark_coverage,
			Self::MarkToLigature(t) => t.mark_coverage,
			Self::MarkToMark(t) => t.mark1_coverage,
			Self::Context(t) => t.coverage(),
			Self::ChainContext(t) => t.coverage(),
		}
	}
}

mod kind01 {
	use super::*;

	/// A [Single Adjustment Positioning Subtable](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#SP).
	#[allow(missing_docs)]
	#[derive(Clone, Copy, Debug)]
	pub enum SingleAdjustment<'a> {
		Format1 {
			coverage: Coverage<'a>,
			value: ValueRecord<'a>,
		},
		Format2 {
			coverage: Coverage<'a>,
			values: ValueRecordsArray<'a>,
		},
	}
	impl<'a> SingleAdjustment<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			match s.read::<u16>()? {
				1 => {
					let coverage = s.parse_at_offset16(data)?;
					let flags = s.read::<ValueFormat>()?;
					let value = ValueRecord::parse(data, &mut s, flags)?;
					Some(Self::Format1 { coverage, value })
				}
				2 => {
					let coverage = s.parse_at_offset16(data)?;
					let flags = s.read::<ValueFormat>()?;
					let count = s.read::<u16>()?;
					let values = ValueRecordsArray::parse(data, count, flags, &mut s)?;
					Some(Self::Format2 { coverage, values })
				}
				_ => None,
			}
		}

		/// Returns the subtable coverage.
		#[inline]
		pub fn coverage(&self) -> Coverage<'a> {
			match self {
				Self::Format1 { coverage, .. } => *coverage,
				Self::Format2 { coverage, .. } => *coverage,
			}
		}
	}

	#[derive(Debug, Clone, Copy, Default)]
	pub struct ValueRecord<'a> {
		pub x_placement: i16,                        // Horizontal adjustment for placement, in design units.
		pub y_placement: i16,                        // Vertical adjustment for placement, in design units.
		pub x_advance: i16,                          // Horizontal adjustment for advance, in design units — only used for horizontal layout.
		pub y_advance: i16,                          // Vertical adjustment for advance, in design units — only used for vertical layout.
		pub x_pla_device_offset: Option<Device<'a>>, // Offset to Device table (non-variable font) / VariationIndex table (variable font) for horizontal placement, from beginning of the immediate parent table (SinglePos or PairPosFormat2 lookup subtable, PairSet table within a PairPosFormat1 lookup subtable) — may be NULL.
		pub y_pla_device_offset: Option<Device<'a>>, // Offset to Device table (non-variable font) / VariationIndex table (variable font) for vertical placement, from beginning of the immediate parent table (SinglePos or PairPosFormat2 lookup subtable, PairSet table within a PairPosFormat1 lookup subtable) — may be NULL.
		pub x_adv_device_offset: Option<Device<'a>>, // Offset to Device table (non-variable font) / VariationIndex table (variable font) for horizontal advance, from beginning of the immediate parent table (SinglePos or PairPosFormat2 lookup subtable, PairSet table within a PairPosFormat1 lookup subtable) — may be NULL.
		pub y_adv_device_offset: Option<Device<'a>>, // Offset to Device table (non-variable font) / VariationIndex table (variable font) for vertical advance, from beginning of the immediate parent table (SinglePos or PairPosFormat2 lookup subtable, PairSet table within a PairPosFormat1 lookup subtable) — may be NULL.
	}
	impl<'a> ValueRecord<'a> {
		pub fn parse(table_data: &'a [u8], s: &mut Stream<'a>, format: ValueFormat) -> Option<Self> {
			Some(Self {
				x_placement: if format.contains(ValueFormat::X_PLACEMENT) { s.read()? } else { 0 },
				y_placement: if format.contains(ValueFormat::Y_PLACEMENT) { s.read()? } else { 0 },
				x_advance: if format.contains(ValueFormat::X_ADVANCE) { s.read()? } else { 0 },
				y_advance: if format.contains(ValueFormat::Y_ADVANCE) { s.read()? } else { 0 },
				x_pla_device_offset: if format.contains(ValueFormat::X_PLACEMENT_DEVICE) {
					s.parse_at_offset16(table_data)
				} else {
					None
				},
				y_pla_device_offset: if format.contains(ValueFormat::Y_PLACEMENT_DEVICE) {
					s.parse_at_offset16(table_data)
				} else {
					None
				},
				x_adv_device_offset: if format.contains(ValueFormat::X_ADVANCE_DEVICE) {
					s.parse_at_offset16(table_data)
				} else {
					None
				},
				y_adv_device_offset: if format.contains(ValueFormat::Y_ADVANCE_DEVICE) {
					s.parse_at_offset16(table_data)
				} else {
					None
				},
			})
		}
	}

	#[derive(Debug, Clone, Copy)]
	pub struct ValueFormat(pub u16);
	impl ValueFormat {
		pub const X_PLACEMENT: ValueFormat = ValueFormat(0x0001); // Includes horizontal adjustment for placement.
		pub const Y_PLACEMENT: ValueFormat = ValueFormat(0x0002); // Includes vertical adjustment for placement.
		pub const X_ADVANCE: ValueFormat = ValueFormat(0x0004); // Includes horizontal adjustment for advance.
		pub const Y_ADVANCE: ValueFormat = ValueFormat(0x0008); // Includes vertical adjustment for advance.
		pub const X_PLACEMENT_DEVICE: ValueFormat = ValueFormat(0x0010); // Includes Device table (non-variable font) / VariationIndex table (variable font) for horizontal placement.
		pub const Y_PLACEMENT_DEVICE: ValueFormat = ValueFormat(0x0020); // Includes Device table (non-variable font) / VariationIndex table (variable font) for vertical placement.
		pub const X_ADVANCE_DEVICE: ValueFormat = ValueFormat(0x0040); // Includes Device table (non-variable font) / VariationIndex table (variable font) for horizontal advance.
		pub const Y_ADVANCE_DEVICE: ValueFormat = ValueFormat(0x0080); // Includes Device table (non-variable font) / VariationIndex table (variable font) for vertical advance.

		// 0xFF00	Reserved	// For future use (set to zero).

		#[inline]
		pub const fn contains(self, other: Self) -> bool {
			self.0 & other.0 == other.0
		}
		#[inline]
		pub fn size(self) -> usize {
			// The high 8 bits are not used, so make sure we ignore them using 0xFF.
			u16::SIZE * (self.0 & 0xFF).count_ones() as usize
		}
	}
	impl FromData for ValueFormat {
		const SIZE: usize = 2;
		fn parse(data: &[u8]) -> Option<Self> {
			Some(Self(u16::parse(data)?))
		}
	}

	/// An array of
	/// [Value Records](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#value-record).
	#[derive(Clone, Copy)]
	pub struct ValueRecordsArray<'a> {
		// We have to store the original table data because ValueRecords can have
		// a offset to Device tables and offset is from the beginning of the table.
		table_data: &'a [u8],
		// A slice that contains all ValueRecords.
		data: &'a [u8],
		// Number of records.
		len: u16,
		// Size of the single record.
		value_len: usize,
		// Flags, used during ValueRecord parsing.
		flags: ValueFormat,
	}

	impl<'a> ValueRecordsArray<'a> {
		fn parse(table_data: &'a [u8], count: u16, flags: ValueFormat, s: &mut Stream<'a>) -> Option<Self> {
			Some(Self {
				table_data,
				flags,
				len: count,
				value_len: flags.size(),
				data: s.read_bytes(usize::from(count) * flags.size())?,
			})
		}
		/// Returns array's length.
		#[inline]
		pub fn len(&self) -> u16 {
			self.len
		}
		/// Checks if the array is empty.
		pub fn is_empty(&self) -> bool {
			self.len == 0
		}
		/// Returns a [`ValueRecord`] at index.
		pub fn get(&self, index: u16) -> Option<ValueRecord<'a>> {
			let start = usize::from(index) * self.value_len;
			let end = start + self.value_len;
			let data = self.data.get(start..end)?;
			let mut s = Stream::new(data);
			ValueRecord::parse(self.table_data, &mut s, self.flags)
		}
		pub fn iter(&self) -> impl Iterator<Item = ValueRecord<'_>> {
			(0..self.len()).into_iter().filter_map(|v| self.get(v))
		}
	}
	impl core::fmt::Debug for ValueRecordsArray<'_> {
		fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
			write!(f, "ValueRecordsArray {{ ... }}")
		}
	}
}
mod kind02 {
	use super::*;

	/// A [Pair Adjustment Positioning Subtable](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#PP).
	#[allow(missing_docs)]
	#[derive(Clone, Copy, Debug)]
	pub enum PairAdjustment<'a> {
		Format1 {
			coverage: Coverage<'a>,
			sets: PairSets<'a>,
		},
		Format2 {
			coverage: Coverage<'a>,
			classes: (ClassDefinition<'a>, ClassDefinition<'a>),
			matrix: ClassMatrix<'a>,
		},
	}
	impl<'a> PairAdjustment<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			match s.read::<u16>()? {
				1 => {
					let coverage = s.parse_at_offset16(data)?;
					let flags = (s.read::<ValueFormat>()?, s.read::<ValueFormat>()?);
					let offsets = s.parse_array16()?;
					Some(Self::Format1 {
						coverage,
						sets: PairSets::new(data, offsets, flags),
					})
				}
				2 => {
					let coverage = s.parse_at_offset16(data)?;
					let flags = (s.read::<ValueFormat>()?, s.read::<ValueFormat>()?);
					let classes = (s.parse_at_offset16(data)?, s.parse_at_offset16(data)?);
					let counts = (s.read::<u16>()?, s.read::<u16>()?);
					Some(Self::Format2 {
						coverage,
						classes,
						matrix: ClassMatrix::parse(data, counts, flags, &mut s)?,
					})
				}
				_ => None,
			}
		}

		/// Returns the subtable coverage.
		#[inline]
		pub fn coverage(&self) -> Coverage<'a> {
			match self {
				Self::Format1 { coverage, .. } => *coverage,
				Self::Format2 { coverage, .. } => *coverage,
			}
		}
	}

	/// A [`ValueRecord`] pairs set used by [`PairAdjustment`].
	#[derive(Clone, Copy)]
	pub struct PairSet<'a> {
		data: &'a [u8],
		flags: (ValueFormat, ValueFormat),
		record_len: u8,
	}

	impl<'a> PairSet<'a> {
		fn parse(data: &'a [u8], flags: (ValueFormat, ValueFormat)) -> Option<Self> {
			let mut s = Stream::new(data);
			let count = s.read::<u16>()?;
			// Max len is 34, so u8 is just enough.
			let record_len = (GlyphId::SIZE + flags.0.size() + flags.1.size()) as u8;
			let data = s.read_bytes(usize::from(count) * usize::from(record_len))?;
			Some(Self { data, flags, record_len })
		}

		#[inline]
		fn binary_search(&self, second: GlyphId) -> Option<&'a [u8]> {
			// Based on Rust std implementation.

			let mut size = self.data.len() / usize::from(self.record_len);
			if size == 0 {
				return None;
			}

			let get_record = |index| {
				let start = index * usize::from(self.record_len);
				let end = start + usize::from(self.record_len);
				self.data.get(start..end)
			};

			let get_glyph = |data: &[u8]| GlyphId(u16::from_be_bytes([data[0], data[1]]));

			let mut base = 0;
			while size > 1 {
				let half = size / 2;
				let mid = base + half;
				// mid is always in [0, size), that means mid is >= 0 and < size.
				// mid >= 0: by definition
				// mid < size: mid = size / 2 + size / 4 + size / 8 ...
				let cmp = get_glyph(get_record(mid)?).cmp(&second);
				base = if cmp == core::cmp::Ordering::Greater { base } else { mid };
				size -= half;
			}

			// base is always in [0, size) because base <= mid.
			let value = get_record(base)?;
			if get_glyph(value).cmp(&second) == core::cmp::Ordering::Equal {
				Some(value)
			} else {
				None
			}
		}

		/// Returns a [`ValueRecord`] pair using the second glyph.
		pub fn get(&self, second: GlyphId) -> Option<(ValueRecord<'a>, ValueRecord<'a>)> {
			let record_data = self.binary_search(second)?;
			let mut s = Stream::new(record_data);
			s.skip::<GlyphId>();
			Some((
				ValueRecord::parse(self.data, &mut s, self.flags.0)?,
				ValueRecord::parse(self.data, &mut s, self.flags.1)?,
			))
		}
		pub fn iter(&'a self) -> impl Iterator<Item = PairValue<'a>> {
			let size = self.data.len() / usize::from(self.record_len);
			(0..size)
				.filter_map(|index| {
					let start = index * usize::from(self.record_len);
					let end = start + usize::from(self.record_len);
					self.data.get(start..end)
				})
				.filter_map(|v| PairValue::parse(v, self.data, self.flags))
		}
	}

	impl core::fmt::Debug for PairSet<'_> {
		fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
			write!(f, "PairSet {{ ... }}")
		}
	}

	#[derive(Debug, Clone)]
	pub struct PairValue<'a> {
		// uint16	secondGlyph	Glyph ID of second glyph in the pair (first glyph is listed in the Coverage table).
		// ValueRecord	valueRecord1	Positioning data for the first glyph in the pair.
		// ValueRecord	valueRecord2	Positioning data for the second glyph in the pair.
		pub second_glyph: GlyphId,
		pub value_record1: ValueRecord<'a>,
		pub value_record2: ValueRecord<'a>,
	}
	impl<'a> PairValue<'a> {
		pub fn parse(data: &'a [u8], base: &'a [u8], format: (ValueFormat, ValueFormat)) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				second_glyph: s.read()?,
				value_record1: ValueRecord::parse(base, &mut s, format.0)?,
				value_record2: ValueRecord::parse(base, &mut s, format.1)?,
			})
		}
	}

	// Essentially a `LazyOffsetArray16` but stores additional data required to parse [`PairSet`].

	/// A list of [`PairSet`]s.
	#[derive(Clone, Copy)]
	pub struct PairSets<'a> {
		data: &'a [u8],
		// Zero offsets must be ignored, therefore we're using `Option<Offset16>`.
		offsets: LazyArray16<'a, Option<Offset16>>,
		flags: (ValueFormat, ValueFormat),
	}

	impl<'a> PairSets<'a> {
		fn new(data: &'a [u8], offsets: LazyArray16<'a, Option<Offset16>>, flags: (ValueFormat, ValueFormat)) -> Self {
			Self { data, offsets, flags }
		}

		/// Returns a value at `index`.
		#[inline]
		pub fn get(&self, index: u16) -> Option<PairSet<'a>> {
			let offset = self.offsets.get(index)??.to_usize();
			self.data.get(offset..).and_then(|data| PairSet::parse(data, self.flags))
		}

		/// Returns array's length.
		#[inline]
		pub fn len(&self) -> u16 {
			self.offsets.len()
		}

		/// Checks if the array is empty.
		pub fn is_empty(&self) -> bool {
			self.offsets.is_empty()
		}
		pub fn iter(&'a self) -> impl Iterator<Item = PairSet<'a>> {
			self.offsets
				.into_iter()
				.filter_map(|v| v)
				.filter_map(|v| self.data.get(v.to_usize()..))
				.filter_map(|v| PairSet::parse(v, self.flags))
		}
	}

	impl core::fmt::Debug for PairSets<'_> {
		fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
			write!(f, "PairSets {{ ... }}")
		}
	}

	/// A [`ValueRecord`] pairs matrix used by [`PairAdjustment`].
	#[derive(Clone, Copy)]
	pub struct ClassMatrix<'a> {
		// We have to store table's original slice,
		// because offsets in ValueRecords are from the begging of the table.
		table_data: &'a [u8],
		matrix: &'a [u8],
		pub counts: (u16, u16),
		flags: (ValueFormat, ValueFormat),
		record_len: u8,
	}

	impl<'a> ClassMatrix<'a> {
		fn parse(table_data: &'a [u8], counts: (u16, u16), flags: (ValueFormat, ValueFormat), s: &mut Stream<'a>) -> Option<Self> {
			let count = (u32::from(counts.0) * u32::from(counts.1)) as usize;
			// Max len is 32, so u8 is just enough.
			let record_len = (flags.0.size() + flags.1.size()) as u8;
			let matrix = s.read_bytes(count * usize::from(record_len))?;
			Some(Self {
				table_data,
				matrix,
				counts,
				flags,
				record_len,
			})
		}

		/// Returns a [`ValueRecord`] pair using specified classes.
		pub fn get(&self, classes: (u16, u16)) -> Option<(ValueRecord<'a>, ValueRecord<'a>)> {
			if classes.0 >= self.counts.0 || classes.1 >= self.counts.1 {
				return None;
			}

			let idx = usize::from(classes.0) * usize::from(self.counts.1) + usize::from(classes.1);
			let record = self.matrix.get(idx * usize::from(self.record_len)..)?;

			let mut s = Stream::new(record);
			Some((
				ValueRecord::parse(self.table_data, &mut s, self.flags.0)?,
				ValueRecord::parse(self.table_data, &mut s, self.flags.1)?,
			))
		}
	}

	impl core::fmt::Debug for ClassMatrix<'_> {
		fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
			write!(f, "ClassMatrix {{ {:?}, ... }}", self.counts)
		}
	}
}
mod kind03 {
	use super::*;

	/// A [Cursive Attachment Positioning Subtable](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#CAP).
	#[allow(missing_docs)]
	#[derive(Clone, Copy, Debug)]
	pub struct CursiveAdjustment<'a> {
		pub coverage: Coverage<'a>,
		pub sets: CursiveAnchorSet<'a>,
	}

	impl<'a> CursiveAdjustment<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			match s.read::<u16>()? {
				1 => Some(Self {
					coverage: s.parse_at_offset16(data)?,
					sets: CursiveAnchorSet {
						data,
						records: s.parse_array16()?,
					},
				}),
				_ => None,
			}
		}
	}

	#[derive(Clone, Copy)]
	struct EntryExitRecord {
		entry_anchor_offset: Option<Offset16>,
		exit_anchor_offset: Option<Offset16>,
	}

	impl FromData for EntryExitRecord {
		const SIZE: usize = 4;

		#[inline]
		fn parse(data: &[u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				entry_anchor_offset: s.read::<Option<Offset16>>()?,
				exit_anchor_offset: s.read::<Option<Offset16>>()?,
			})
		}
	}

	/// A list of entry and exit [`Anchor`] pairs.
	#[derive(Clone, Copy)]
	pub struct CursiveAnchorSet<'a> {
		data: &'a [u8],
		records: LazyArray16<'a, EntryExitRecord>,
	}

	impl<'a> CursiveAnchorSet<'a> {
		/// Returns an entry [`Anchor`] at index.
		pub fn entry(&self, index: u16) -> Option<Anchor<'a>> {
			let offset = self.records.get(index)?.entry_anchor_offset?.to_usize();
			self.data.get(offset..).and_then(Anchor::parse)
		}

		/// Returns an exit [`Anchor`] at index.
		pub fn exit(&self, index: u16) -> Option<Anchor<'a>> {
			let offset = self.records.get(index)?.exit_anchor_offset?.to_usize();
			self.data.get(offset..).and_then(Anchor::parse)
		}

		/// Returns the number of items.
		pub fn len(&self) -> u16 {
			self.records.len()
		}

		/// Checks if the set is empty.
		pub fn is_empty(&self) -> bool {
			self.records.is_empty()
		}
		pub fn iter(&self) -> impl Iterator<Item = (Option<Anchor<'_>>, Option<Anchor<'_>>)> {
			(0..self.len()).into_iter().map(|i| (self.entry(i), self.exit(i)))
		}
	}

	impl core::fmt::Debug for CursiveAnchorSet<'_> {
		fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
			write!(f, "CursiveAnchorSet {{ ... }}")
		}
	}
}
mod kind04 {
	use super::*;

	/// A [Mark-to-Base Attachment Positioning Subtable](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MBP).
	#[derive(Clone, Copy, Debug)]
	pub struct MarkToBaseAdjustment<'a> {
		/// A mark coverage.
		pub mark_coverage: Coverage<'a>,
		/// A base coverage.
		pub base_coverage: Coverage<'a>,
		/// A list of mark anchors.
		pub marks: MarkArray<'a>,
		/// An anchors matrix.
		pub anchors: AnchorMatrix<'a>,
	}

	impl<'a> MarkToBaseAdjustment<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			match s.read::<u16>()? {
				1 => {
					let mark_coverage = s.parse_at_offset16(data)?;
					let base_coverage = s.parse_at_offset16(data)?;
					let class_count = s.read::<u16>()?;
					let marks = s.parse_at_offset16(data)?;
					let anchors = AnchorMatrix::parse(s.read_at_offset16(data)?, class_count)?;
					Some(Self {
						mark_coverage,
						base_coverage,
						marks,
						anchors,
					})
				}
				_ => None,
			}
		}
	}

	#[derive(Clone, Copy)]
	struct MarkRecord {
		class: u16,
		mark_anchor: Offset16,
	}
	impl FromData for MarkRecord {
		const SIZE: usize = 4;
		#[inline]
		fn parse(data: &[u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				class: s.read::<u16>()?,
				mark_anchor: s.read::<Offset16>()?,
			})
		}
	}

	/// A [Mark Array](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table).
	#[derive(Clone, Copy)]
	pub struct MarkArray<'a> {
		data: &'a [u8],
		array: LazyArray16<'a, MarkRecord>,
	}
	impl<'a> FromSlice<'a> for MarkArray<'a> {
		fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				data,
				array: s.parse_array16()?,
			})
		}
	}
	impl<'a> MarkArray<'a> {
		/// Returns contained data at index.
		pub fn get(&self, index: u16) -> Option<(u16, Anchor<'a>)> {
			let record = self.array.get(index)?;
			let anchor = self.data.get(record.mark_anchor.to_usize()..).and_then(Anchor::parse)?;
			Some((record.class, anchor))
		}
		/// Returns the array length.
		pub fn len(&self) -> u16 {
			self.array.len()
		}
		/// Checks if the array is empty.
		pub fn is_empty(&self) -> bool {
			self.array.is_empty()
		}
	}

	impl core::fmt::Debug for MarkArray<'_> {
		fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
			write!(f, "MarkArray {{ ... }}")
		}
	}
}
mod kind05 {
	use super::*;

	/// A [Mark-to-Ligature Attachment Positioning Subtable](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MLP).
	#[allow(missing_docs)]
	#[derive(Clone, Copy, Debug)]
	pub struct MarkToLigatureAdjustment<'a> {
		pub mark_coverage: Coverage<'a>,
		pub ligature_coverage: Coverage<'a>,
		pub marks: MarkArray<'a>,
		pub ligature_array: LigatureArray<'a>,
	}

	impl<'a> MarkToLigatureAdjustment<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			match s.read::<u16>()? {
				1 => {
					let mark_coverage = s.parse_at_offset16(data)?;
					let ligature_coverage = s.parse_at_offset16(data)?;
					let class_count = s.read::<u16>()?;
					let marks = s.parse_at_offset16(data)?;
					let ligature_array = LigatureArray::parse(s.read_at_offset16(data)?, class_count)?;
					Some(Self {
						mark_coverage,
						ligature_coverage,
						marks,
						ligature_array,
					})
				}
				_ => None,
			}
		}
	}

	/// An array or ligature anchor matrices.
	#[derive(Clone, Copy)]
	pub struct LigatureArray<'a> {
		data: &'a [u8],
		class_count: u16,
		offsets: LazyArray16<'a, Offset16>,
	}
	impl<'a> LigatureArray<'a> {
		fn parse(data: &'a [u8], class_count: u16) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				data,
				class_count,
				offsets: s.parse_array16()?,
			})
		}
		/// Returns an [`AnchorMatrix`] at index.
		pub fn get(&self, index: u16) -> Option<AnchorMatrix<'a>> {
			let offset = self.offsets.get(index)?.to_usize();
			let data = self.data.get(offset..)?;
			AnchorMatrix::parse(data, self.class_count)
		}
		/// Returns the array length.
		pub fn len(&self) -> u16 {
			self.offsets.len()
		}
		/// Checks if the array is empty.
		pub fn is_empty(&self) -> bool {
			self.offsets.is_empty()
		}
	}
	impl core::fmt::Debug for LigatureArray<'_> {
		fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
			write!(f, "LigatureArray {{ ... }}")
		}
	}
}
mod kind06 {
	use super::*;

	/// A [Mark-to-Mark Attachment Positioning Subtable](
	/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MMP).
	#[allow(missing_docs)]
	#[derive(Clone, Copy, Debug)]
	pub struct MarkToMarkAdjustment<'a> {
		pub mark1_coverage: Coverage<'a>,
		pub mark2_coverage: Coverage<'a>,
		pub marks: MarkArray<'a>,
		pub mark2_matrix: AnchorMatrix<'a>,
	}

	impl<'a> MarkToMarkAdjustment<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			match s.read::<u16>()? {
				1 => {
					let mark1_coverage = s.parse_at_offset16(data)?;
					let mark2_coverage = s.parse_at_offset16(data)?;
					let class_count = s.read::<u16>()?;
					let marks = s.parse_at_offset16(data)?;
					let mark2_matrix = AnchorMatrix::parse(s.read_at_offset16(data)?, class_count)?;
					Some(Self {
						mark1_coverage,
						mark2_coverage,
						marks,
						mark2_matrix,
					})
				}
				_ => None,
			}
		}
	}

	/// An [Anchor Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-tables).
	///
	/// The *Anchor Table Format 2: Design Units Plus Contour Point* is not supported.
	#[derive(Clone, Copy, Debug)]
	pub struct Anchor<'a> {
		/// Horizontal value, in design units.
		pub x: i16,
		/// Vertical value, in design units.
		pub y: i16,
		/// A [`Device`] table with horizontal value.
		pub x_device: Option<Device<'a>>,
		/// A [`Device`] table with vertical value.
		pub y_device: Option<Device<'a>>,
	}
	impl<'a> Anchor<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let format = s.read::<u16>()?;
			if !matches!(format, 1..=3) {
				return None;
			}
			let mut table = Anchor {
				x: s.read::<i16>()?,
				y: s.read::<i16>()?,
				x_device: None,
				y_device: None,
			};
			// Note: Format 2 is not handled since there is currently no way to
			// get a glyph contour point by index.
			if format == 3 {
				table.x_device = s
					.read::<Option<Offset16>>()?
					.and_then(|offset| data.get(offset.to_usize()..))
					.and_then(Device::parse);

				table.y_device = s
					.read::<Option<Offset16>>()?
					.and_then(|offset| data.get(offset.to_usize()..))
					.and_then(Device::parse);
			}
			Some(table)
		}
	}

	/// An [`Anchor`] parsing helper.
	#[derive(Clone, Copy)]
	pub struct AnchorMatrix<'a> {
		data: &'a [u8],
		/// Number of rows in the matrix.
		pub rows: u16,
		/// Number of columns in the matrix.
		pub cols: u16,
		matrix: LazyArray32<'a, Option<Offset16>>,
	}
	impl<'a> AnchorMatrix<'a> {
		pub fn parse(data: &'a [u8], cols: u16) -> Option<Self> {
			let mut s = Stream::new(data);
			let rows = s.read::<u16>()?;
			let count = u32::from(rows) * u32::from(cols);
			let matrix = s.read_array32(count)?;
			Some(Self { data, rows, cols, matrix })
		}
		/// Returns an [`Anchor`] at position.
		pub fn get(&'_ self, row: u16, col: u16) -> Option<Anchor<'_>> {
			let idx = u32::from(row) * u32::from(self.cols) + u32::from(col);
			let offset = self.matrix.get(idx)??.to_usize();
			Anchor::parse(self.data.get(offset..)?)
		}
	}
	impl core::fmt::Debug for AnchorMatrix<'_> {
		fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
			write!(f, "AnchorMatrix {{ ... }}")
		}
	}
}
